using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.ServiceComponent.Error;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.Base.Entity;

namespace T.FAS.Runtime.DataAccess.Engine
{
    class ESSelectSqlBuildStrategy : BaseSqlBuildStrategy
    {
        public override SqlStatementCollection BuildTableSqlSchema(SqlBuildingContext context)
        {
            if (context == null)
                throw new FasException(DataAccessErrorCode.Retrieve.NullValue, CommonStringClassEngine.SqlBuildStrategy_ContextCannotBeNull);

            var sqls = new SqlStatementCollection();

            var selectStatement = SQLStatementFactory.CreateSqlStatement(SqlStatementType.Select, context.DbType) as SelectSqlStaForES;
            selectStatement.SqlBuildingInfo.DataSource = context.DataSource;

            //Parsing main part of query SQL statement
            BuildMainFrom(selectStatement, context.CommonObject, context.Node, context.DataObject, context.TableName, context);

            //Parsing query fields list
            BuildQueryFieldList(selectStatement, context.Node, context.DataObject, context.QueryFilter.ProjectionFields);           

            sqls.Add(selectStatement);

            return sqls;
        }

        public override void BuildTableSqlDetail(SqlStatementCollection sqls, SqlBuildingContext context)
        {
            var sql = sqls.FirstOrDefault(x => x.SqlBuildingInfo.TableName == context.TableName);
            if (sql == null) return;

            var querySQL = sql as SelectSqlStaForES;

            //Parsing where clause of query SQL statement
            BuildSQLCondition(querySQL, context);
        }

        #region 构造SQL主干        

        /// <summary>
        /// 构造查询主体部分
        /// </summary>
        /// <remarks>查询主体是一个子查询，为了支持一个数据对象多个数据库表</remarks>
        /// <param name="sql">Select语句</param>
        /// <param name="CompositeDataObject">通用中间对象</param>
        /// <param name="CompositeNodeObject">当前节点</param>
        /// <param name="dataObject">当前节点对应的数据对象</param>
        private void BuildMainFrom(SelectSqlStatement sql, CompositeDataObject CompositeDataObject, CompositeNodeObject CompositeNodeObject, DataObject dataObject, string tableName, SqlBuildingContext sqlContext)
        {
            //初始化主查询SQL语句
            var context = sqlContext.DataContext;
            sql.NodeID = CompositeNodeObject.ID;
            sql.CommonObjectID = CompositeDataObject.ID;
            sql.NodeVersion = CompositeDataObject.Version.ToString();
            sql.CommonObjectVersion = CompositeDataObject.Version.ToString();
            sql.TableName = tableName;
            sql.TableCode = tableName;
            sql.SqlBuildingInfo = base.InitSqlBuildingInfo(CompositeDataObject, CompositeNodeObject, dataObject, tableName, sqlContext.DataSource);

            if (context.Data.ContainsKey(CompositeNodeObject.ID))
            {
                var dataContextItem = context.GetCurrentDataContextItem(CompositeNodeObject.ID);
                foreach (var column in dataObject.PKColumns)
                {
                    var pkField = new SqlPrimaryKeyField(sql.SqlBuildingInfo.CurrentSqlTable, column.ColumnName);
                    var pkElement = CompositeNodeObject.Elements.FirstOrDefault(i => i.DataColumnID == column.ID);
                    pkField.Value.Value = dataContextItem.PrimaryKeyData[pkElement.ID];
                    sql.PrimaryKeys.ChildCollection.Add(pkField);
                }
            }

            //将子查询加入到当前Select语句中
            sql.From.ChildCollection.Add(sql.SqlBuildingInfo.CurrentSqlTable);
        }

        /// <summary>
        /// 构造处理Select语句中查询字段部分
        /// </summary>
        /// <param name="sql">Select语句</param>
        /// <param name="co">通用中间对象</param>
        /// <param name="CompositeNodeObject">当前节点对象</param>
        /// <param name="dataObject">当前节点对应的数据对象</param>
        public void BuildQueryFieldList(SelectSqlStatement sql, CompositeNodeObject CompositeNodeObject, DataObject dataObject, List<QueryProjectionField> projectionFilelds)
        {
            if (projectionFilelds == null || projectionFilelds.Count == 0)
            {
                var elements = CompositeNodeObject.Elements.Where(i => i.ElementType != ElementType.Virtual || i.ElementType != ElementType.Reference);

                foreach (var element in elements)
                {
                    if (string.IsNullOrEmpty(element.DataColumnID)) continue;
                    var column = dataObject.Columns.FirstOrDefault(i => i.ID == element.DataColumnID);
                    if (column == null)
                        throw new FasException(DataAccessErrorCode.CompositeDataObject.ColumnError,
                             string.Format(CommonStringClassEngine.DataObject_CannotFindColumn, dataObject.Name, element.DataColumnID));

                    var field = new SelectListField()
                    {
                        Table = sql.SqlBuildingInfo.CurrentSqlTable,
                        IsUseAlias = false,
                        FieldName = column.ColumnName,
                        FieldAlias = element.Alias
                    };
                    sql.SelectList.ChildCollection.Add(field);
                }
            }
            else
            {
                foreach (var projectionField in projectionFilelds)
                {
                    var field = new SelectListField()
                    {
                        Table = sql.SqlBuildingInfo.CurrentSqlTable,
                        IsUseAlias = false,                        
                    };

                    BuildSelectListField(CompositeNodeObject, dataObject, field, projectionField);
                    sql.SelectList.ChildCollection.Add(field);
                }
            }               
        }
        private void BuildSelectListField(CompositeNodeObject CompositeNodeObject, DataObject dataObject, SelectListField field, QueryProjectionField projectionField)
        {
            switch (projectionField.FieldType)
            {
                case FieldType.Column:
                    field.FieldName = projectionField.Field;
                    field.FieldAlias = projectionField.Field;
                    break;
                case FieldType.Element:
                    var element = CompositeNodeObject.Elements.FirstOrDefault(i => i.Name == projectionField.Field);
                    if (element == null || string.IsNullOrEmpty(element.DataColumnID))
                    {
                        throw new FasException(DataAccessErrorCode.CompositeDataObject.ElementError,
                             string.Format(CommonStringClassEngine.ESSelectSqlBuildStrategy_BuildSelectListField, CompositeNodeObject.Name, projectionField.Field));
                    }
                    var column = dataObject.Columns.FirstOrDefault(i => i.ID == element.DataColumnID);
                    if (column == null)
                        throw new FasException(DataAccessErrorCode.CompositeDataObject.ColumnError,
                              string.Format(CommonStringClassEngine.DataObject_CannotFindColumn, dataObject.Name, element.DataColumnID));
                    field.FieldName = column.ColumnName;
                    field.FieldAlias = element.Alias;
                    break;
                case FieldType.FunctionExpression:
                    field.FieldName = projectionField.Field;
                    field.FieldAlias = projectionField.Field;                    
                    break;
            }
        }

        #endregion

        /// <summary>
        /// 构造Select语句中过滤条件
        /// </summary>          
        /// <param name="sql">Sql语句对象</param>       
        /// <param name="context">SQL构造上下文</param>
        public void BuildSQLCondition(SelectSqlStatement sql, SqlBuildingContext context)
        {
            // 解析授权访问控制的约束条件:主表的查询支持权限关联条件。
            this.ParseSecurityCondition(sql, context);

            // 解析外部传入的过滤条件（表单、帮助、报表等）,注:只处理主对象上的过滤条件。         
            base.GetSelectSqlCondition(sql, context, sql.SqlBuildingInfo.RootNode, sql.SqlBuildingInfo.RootDataObject);                     

            // 解析过滤条件
            sql.FilterCondition.ChildCollection.Add(base.ParseFilterCondition(sql, context));

            // 解析排序条件
            sql.OrderByCondition = base.ParseOrderByCondition(context);

            // 获取前多少条数据。
            sql.TopSize = context.QueryFilter.LimitCount;

            // 分页设置
            if (context.QueryFilter != null && context.QueryFilter.PageParameter != null)
            {
                sql.PageCount = context.QueryFilter.PageParameter.PageSize;
                sql.PageIndex = context.QueryFilter.PageParameter.CurrentPageIndex;
            }
        }
    }
}
